package org.hzero.business;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

/**
 * @ClassName: DateUtil
 * @Description: 日期工具类
 * @author: tianhao.luo@hand-china.com
 * @date: 2020/9/25  10:35
 */
public final class KnifeDateUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(KnifeDateUtil.class);

    /**
     * 获取逾期的时间，返回的float
     * @param standardDate 开始时间
     * @param endDate 结束时间
     * @return 结束时间-开始时间的天数float值
     */
    public static float getOverdueDay(Date standardDate, Date endDate){
        Assert.notNull(standardDate,"时间参数不能为空！");
        Assert.notNull(endDate,"时间参数不能为空！");
        long endDateTime = endDate.getTime();
        //计划结束时间加一天
        Instant instant = standardDate.toInstant().plus(Duration.ofDays(1));
        Date from = Date.from(instant);
        long standardDateTime = from.getTime();
        return  (float) (endDateTime - standardDateTime) / (1000 * 60 * 60 * 24);

    }

    /**
     * 获取逾期时间的字符串，且取小数点后两位
     * @param overdueTime 天数float类型的时间
     * @return 时间转化为天
     */
    public static String getOverdueDayStr(float overdueTime){
        DecimalFormat decimalFormat=new DecimalFormat("0.00");
        String format = decimalFormat.format(overdueTime);
        if (format.startsWith("0")) {
            format="1";
        }
        return format;
    }

    /**
     * 获取当前时间前或后limitDay天的时间字符串
     * @param flag true为前limitDay天，否则为后limitDay天
     * @param limitDay 指定天数
     * @param pattern 指定时间格式yyyy-MM-dd HH:mm:ss
     * @return 时间字符串
     */
    public static String getOrderDayDateString(boolean flag, Long limitDay,String pattern) {
        Assert.isTrue(!StringUtils.isEmpty(pattern),"时间格式不能为空！");
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
        Instant now = Instant.now();
        Duration duration = Duration.ofDays(limitDay);
        Date from = null;
        try {
            if (flag) {
                Instant minus = now.minus(duration);
                from = Date.from(minus);

            } else {
                Instant plus = now.plus(duration);
                from = Date.from(plus);
            }
        } catch (Exception e) {
            LOGGER.error("{}格式无法格式化Data类型时间！",pattern);
        }
        return simpleDateFormat.format(from);
    }

    /**
     * 获取当前时间前或后limitDay天的时间
     * @param flag true为前limitDay天，否则为后limitDay天
     * @param limitDay 指定天数
     * @return 时间
     */
    public static Date getOrderDayDate(boolean flag, Long limitDay) {
        Instant now = Instant.now();
        Duration duration = Duration.ofDays(limitDay);
        Date from = null;
        if (flag) {
            Instant minus = now.minus(duration);
            from = Date.from(minus);

        } else {
            Instant plus = now.plus(duration);
            from = Date.from(plus);
        }
        return from;
    }

    /**
     * 标准时间大的返回false
     *
     * @param standard   标准时间
     * @param beCompared 被比较时间
     * @return 标准时间大的返回false
     */
    public static Boolean compareDateTimeBoolean(Date standard, Date beCompared) {
        Assert.notNull(standard,"标准时间不能为空");
        return standard.compareTo(beCompared) <= 0;
    }

    /**
     * 标准时间大的返回1
     *
     * @param standard   标准时间
     * @param beCompared 被比较时间
     * @return 标准时间大的返回1
     */
    public static Integer compareDateTimeInteger(Date standard, Date beCompared) {
        Assert.notNull(standard,"标准时间不能为空");
        return standard.compareTo(beCompared) <= 0 ? 0 : 1;
    }

    /**
     * 有可能为空的参数判断是否相等判断，相等返回true，不想等返回false
     *
     * @param t   参数1
     * @param s   参数2
     * @param <T> 占位符
     * @param <S> 占位符
     * @return 都为空为true，不都为空为时判断是否相等
     */
    public static <T, S> Boolean compareWillNull(T t, S s) {
        //两个都不为空时，走equals判断
        if (t != null && s != null) {
            return t.equals(s);
        } else {
            //存在空，必须两者都为空，才返回true
            return t == null && s == null;
        }
    }


    /**
     * 判断时间段是否有交集
     */
    private boolean judgeIntersection(Date firstOrderStartDate,
                                      Date firstOrderFinishDate,
                                      Date secondOrderStartDate,
                                      Date secondOrderFinishDate) {
        // 判断是否有交集
        // 1 先找到最先开始的
        if (firstOrderStartDate.before(secondOrderStartDate)) {
            // 第一张单据开始时间早于第二张单据
            // 那么判断第一张单据的结束时间是否早于第二张单据的开始时间
            // 如果早于,则没有交集;如果不是,则有交集
            return !firstOrderFinishDate.before(secondOrderStartDate);
        } else {
            // 如果第一张单据晚于第二张单据的开始时间
            // 那么判断第二章单据的结束是否是否早于第一张单据的开始时间
            // 如果是,则没有交集
            return !secondOrderFinishDate.before(firstOrderStartDate);
        }
    }

    interface DateFunction{
        <T> Date getOfObj(T t);
    }


    /**
     * 清理list内部数据，合并时间
     * <p>
     * 算法要求：结果中所有的记录的时间不会有交集
     *
     * @param list 内部数据
     */
    public <T> void scanAndUnionResultList(List<T> list,
                                           DateFunction firstStartDateFunction,
                                           DateFunction firstEndDateFunction,
                                           DateFunction secondStartDateFunction,
                                           DateFunction secondEndDateFunction) {
        /**
         *
         * 将下面的排序部分移动到插入元素处额
         *
         *  // 记录按开始时间进行排序，从小到大
         *  // 这个排序逻辑很关键，它决定了我们从第一元素开始向后扫描出交集合并后，list中第二个元素开始第二轮向后扫描会不会出现和list中第一个元素产生交集
         *  list.sort(new Comparator<WoAnalysisResult>() {
         *      @Override
         *      public int compare(WoAnalysisResult o1, WoAnalysisResult o2) {
         *          if (o1.getActualStartDate().before(o2.getActualStartDate())) {
         *              return -1;
         *          } else if (o1.getActualStartDate().equals(o2.getActualStartDate())) {
         *              return 0;
         *          } else {
         *              return 1;
         *          }
         *      }
         *  });
         */
        HashSet<Integer> skipIndexSet = new HashSet<>();
        // 这段算法是下面for循环的变种，减少了下面for循环中i位置如果需要跳过时还继续走循环了
        int size = list.size();
        int tempIndex = 0;
        // 如果记录之间有交集，那么第j个元素一定可以被干掉
        while (tempIndex < size - 1) {
            int nextElementIndex = tempIndex + 1;
            while (!skipIndexSet.contains(tempIndex) && nextElementIndex < size){
                if (!skipIndexSet.contains(nextElementIndex) &&
                        judgeIntersection(firstStartDateFunction.getOfObj(list.get(tempIndex)), firstEndDateFunction.getOfObj(list.get(tempIndex)),
                                secondStartDateFunction.getOfObj(list.get(nextElementIndex)), secondEndDateFunction.getOfObj(list.get(nextElementIndex)))) {
                    unionDate(list.get(tempIndex), list.get(nextElementIndex));
                    skipIndexSet.add(nextElementIndex);
                }
                nextElementIndex++;
            }
            tempIndex++;
        }


        /**
         * // 如果记录之间有交集，那么第j个元素一定可以被干掉
         * for (int i = 0; i < list.size() - 1; i++) {
         *     for (int j = i + 1; j < list.size(); j++) {
         *         // 保证i这个位置的记录的时间取自所有后面的所有记录的交集
         *         if (!skipIndexSet.contains(j) && !skipIndexSet.contains(i) &&
         *                 judgeIntersection(list.get(i).getActualStartDate(), list.get(i).getActualFinishDate(),
         *                         list.get(j).getActualStartDate(), list.get(j).getActualFinishDate())) {
         *             unionDate(list.get(i), list.get(j));
         *             skipIndexSet.add(j);
         *         }
         *     }
         * }
         */

        // 移除被合并的元素
        for (Integer rKey : skipIndexSet) {
            list.remove(rKey.intValue());
        }
        /**
         * list内部自己扫描,判断元素是否出现交集,有交集就合并,然后删除另一个元素
         *
         * 下面这段逻辑是最开始的方案，会出现合并不彻底的bug，主要原因就是当list个数为奇数个，最后一个元素不能继续匹配
         * Iterator<WoAnalysisResult> iterator = list.iterator();
         *         while (iterator.hasNext()) {
         *             WoAnalysisResult next = iterator.next();
         *             // 这里的代码有些情况没有考虑到需要合并，例如list中有3个元素，前两个没有交集，现在到了第三个元素了，
         *             // 但是iterator.hasNext()也没有值，最后一个元素就没有进行处理
         *             if (iterator.hasNext()) {
         *                 WoAnalysisResult nextNext = iterator.next();
         *                 if (judgeIntersection(next.getActualStartDate(), next.getActualFinishDate(), nextNext.getActualStartDate(), nextNext.getActualFinishDate())) {
         *                     unionDate(next, nextNext);
         *                     iterator.remove();
         *                 }
         *             }
         *         }
         */

    }

    /**
     * 合并分析记录中的时间
     * <p>
     * 简单粗暴的合并操作
     *
     * @param woAnalysisResult     分析结果
     * @param nextWoAnalysisResult 分析结果
     */
    private <T> void unionDate(T woAnalysisResult, T nextWoAnalysisResult) {
        /**
         * // 合并开始时间
         *         Date analysisStartDate = woAnalysisResult.getActualStartDate();
         *         Date woStartDate = nextWoAnalysisResult.getActualStartDate();
         *         // 取最先时间
         *         woAnalysisResult.setActualStartDate(woStartDate.before(analysisStartDate) ? woStartDate : analysisStartDate);
         *         // 合并结束时间
         *         Date analysisFinishDate = woAnalysisResult.getActualFinishDate();
         *         Date woFinishDate = nextWoAnalysisResult.getActualFinishDate();
         *         woAnalysisResult.setActualFinishDate(woFinishDate.after(analysisFinishDate) ? woFinishDate : analysisFinishDate);
         */
    }
}
